#version 420

// Interpolated values from the vertex shaders
in vec2 UV;	// texture coord
in float high;	// high of terrain
in vec3 Position_worldspace;
in vec3 Normal_cameraspace;
in vec3 EyeDirection_cameraspace;
in vec3 LightDirection_cameraspace;

in vec4 ShadowCoord; // shadow map coordinates

uniform sampler2DShadow shadowMap;

uniform sampler2D ta; // sand
uniform sampler2D tb; // grass
uniform sampler2D tc; // highmap
uniform sampler2D td; // snow
uniform sampler2D te; // underwater
uniform sampler2D tf; // underwater
uniform sampler2D road; // road

uniform mat4 MV;

uniform vec3 lightAmb;	// ambient color of light
uniform vec3 lightDif;	// diffuse light color
uniform vec3 lightSpec; // specular color of light
uniform vec3 LightPosition_worldspace; // light position in world space
uniform float WS;	// water surface y coord
uniform int clip; // clipping mode


// output from shader
out vec3 color;

const int HCON = 10000;


vec2 poissonDisk[16] = vec2[]( 
   vec2( -0.094201624, -0.039906216 ), 
   vec2( 0.094558609, -0.076890725 ), 
   vec2( -0.0094184101, -0.092938870 ), 
   vec2( 0.034495938, 0.029387760 ), 
   vec2( -0.091588581, 0.045771432 ), 
   vec2( -0.081544232, -0.087912464 ), 
   vec2( -0.038277543, 0.027676845 ), 
   vec2( 0.097484398, 0.075648379 ), 
   vec2( 0.044323325, -0.097511554 ), 
   vec2( 0.053742981, -0.047373420 ), 
   vec2( -0.026496911, -0.041893023 ), 
   vec2( 0.079197514, 0.019090188 ), 
   vec2( -0.024188840, 0.099706507 ), 
   vec2( -0.081409955, 0.091437590 ), 
   vec2( 0.019984126, 0.078641367 ), 
   vec2( 0.014383161, -0.014100790 ) 
);

// calculate lightning
// impute : amb, dif and spec material color
vec3 calculateLightning( vec3 MaterialAmbiantColor, 
					     vec3 MaterialDiffuseColor, 
					     vec3 MaterialSpecularColor )
{
	float lightPower = 1000.0f;

	// Normal of the computed fragment, in camera space
	vec3 n = normalize( Normal_cameraspace );
	// Direction of the light (from the fragment to the light)
	vec3 l = normalize( LightDirection_cameraspace );

	float cosTheta = clamp( dot( n,l ), 0,1 );

	// Eye vector (towards the camera)
	vec3 E = normalize(-EyeDirection_cameraspace);
	// Direction in which the triangle reflects the light
	vec3 R = reflect(-l,n);
	// Cosine of the angle between the Eye vector and the Reflect vector,
	float cosAlpha = clamp( dot( E,R ), 0,1 );

	return 	MaterialAmbiantColor + 
		   	// Diffuse : "color" of the object
			MaterialDiffuseColor * lightDif  * cosTheta +
		    // Specular : reflective highlight, like a mirror
		    MaterialSpecularColor * lightSpec  * pow(cosAlpha,5);
}

// Returns a random number based on a vec3 and an int.
float random(vec3 seed, int i){
	vec4 seed4 = vec4(seed,i);
	float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673));
	return fract(sin(dot_product) * 43758.5453);
}

// return visibility of fragment
// using shadow map
float calculateShadows()
{
	float visibility = 1.0;
	float bias = 0.0005;
	
 	// Sample the shadow map 4 times
	for (int i=0;i<4;i++){
		// use either :
		//  - Always the same samples.
		//    Gives a fixed pattern in the shadow, but no noise
		//  - A random sample, based on the pixel's screen location. 
		//    No banding, but the shadow moves with the camera, which looks weird.
		//int index = int(16.0*random(gl_FragCoord.xyy, i))%16;
		//  - A random sample, based on the pixel's position in world space.
		//    The position is rounded to the millimeter to avoid too much aliasing
 		int index = int(16.0*random(floor(Position_worldspace.xyz*1000.0), i))%16;
		
		// being fully in the shadow will eat up 4*0.2 = 0.8
		// 0.2 potentially remain, which is quite dark.
		visibility -= 0.15*(1.0-texture( shadowMap, vec3(ShadowCoord.xy + poissonDisk[index]/150.0,  (ShadowCoord.z-bias)/ShadowCoord.w) ));


	}
	
	//visibility -= 0.5*(1.0-texture( shadowMap, vec3(ShadowCoord.xy,  (ShadowCoord.z-bias)/ShadowCoord.w) ));
	
	
	return visibility;
}

void main() 
{

    vec4 texel0, texel1, texel2, texel3, texel4, texel5, mixt, mixta, roadtexel;
    float highbuf;


	if (clip != 2)
	{
		if ((clip == 0) && (high < WS))
			discard;

		if ((clip == 1) && (high >= WS+0.5))
			discard;
	}


	texel0 = texture2D(ta, UV/6); // sand
	texel1 = texture2D(tb, UV/8); // grass
	texel2 = texture2D(tc, UV/8); // stone
	texel3 = texture2D(td, UV/8); // snow
	texel4 = texture2D(te, UV/8); // underwater
	texel5 = texture2D(tf, UV/250); // deformation
	//roadtexel = texture2D(road, UV/2); // road
	
	texel4.a = 1.0;
	texel0.a = 0.0;
	texel1.a = 0.0;
	texel2.a = 0.0;
	texel3.a = 0.0;	
	
	int h = int(high * HCON);
	
	if (h > 12000)
	{
		texel4.a = 1.0-((high-1.2)*1.43);
		texel0.a = 0.0+((high-1.2)*1.43);
	}
	
	if (h > 18000)
	{
		texel0.a = 1.0;
		texel4.a = 0.0;
	}	
	
	if (h > 18500) // sand to grass
	{
		texel0.a = 1.0-((high-1.85)*6.4);
		texel1.a = 0.0+((high-1.85)*6.4);
	}
		
	if (h > 20000) // grass
	{
		texel0.a = 0.0;
		texel1.a = 1.0;
	}
		
	if (h > 60000) // grass to stone
	{
		texel1.a = 1.0-((high-6.0)*0.33);
		texel2.a = 0.0+((high-6.0)*0.33);
	}
		
	if (h > 90000) // stone
	{
		texel1.a = 0.0;
		texel2.a = 1.0;
	}
		
	if (h > 100000) // stone to snow
	{
		texel2.a = 1.0-((high-10.0)*2.0);
		texel3.a = 0.0+((high-10.0)*2.0);
	}
		
	if (h > 105000) // snow
	{
		texel2.a = 0.0;
		texel3.a = 1.0;
	}


		
	mixt = (texel4 * texel4.a) + (texel0 * texel0.a) + (texel1 * texel1.a ) + (texel2 * texel2.a) + (texel3 * texel3.a);	

	//mixt = textel0 * highbuf + texel1 * highbuf;
	
	
	//if ((high > 1.80) && (high < 1.95))
	//  texel5.a = texel5.a - (1.0 - ((high-1.80) * 6.4));
	
	if (high > 1.80)
	  mixt = mix(mixt, texel5, texel5.a/2);
	
	//mixt = mix (mixt, roadtexel,texel5.a);
	
	// Material properties
	vec3 MaterialDiffuseColor = mixt.rgb;
	vec3 MaterialAmbiantColor = vec3(0.2,0.2,0.2) * MaterialDiffuseColor;
	vec3 MaterialSpecularColor = vec3(0.01,0.01,0.01);

	

	if (clip == 1)
		color = calculateLightning(MaterialAmbiantColor,MaterialDiffuseColor, MaterialSpecularColor);
	else
		color = calculateLightning(MaterialAmbiantColor,MaterialDiffuseColor, MaterialSpecularColor) * calculateShadows();
}
